Authors

EXERCISE:

Apply PDP to the regression example of predicting bike rentals. Fit a random forest approximation for the prediction of bike rentals (cnt). Use the partial dependence plot to visualize the relationships the model learned. Use the slides shown in class as model.

QUESTION:

Analyse the influence of days since 2011, temperature, humidity and wind speed on the predicted bike counts.

library(dplyr)
package 㤼㸱dplyr㤼㸲 was built under R version 4.0.5
Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
library(plotly)
package 㤼㸱plotly㤼㸲 was built under R version 4.0.5Loading required package: ggplot2
package 㤼㸱ggplot2㤼㸲 was built under R version 4.0.5Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

Attaching package: 㤼㸱plotly㤼㸲

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    last_plot

The following object is masked from 㤼㸱package:stats㤼㸲:

    filter

The following object is masked from 㤼㸱package:graphics㤼㸲:

    layout
library(reshape2)
package 㤼㸱reshape2㤼㸲 was built under R version 4.0.5
library(lubridate)
package 㤼㸱lubridate㤼㸲 was built under R version 4.0.5
Attaching package: 㤼㸱lubridate㤼㸲

The following objects are masked from 㤼㸱package:base㤼㸲:

    date, intersect, setdiff, union
library(randomForestSRC)
package 㤼㸱randomForestSRC㤼㸲 was built under R version 4.0.5
 randomForestSRC 3.1.0 
 
 Type rfsrc.news() to see new features, changes, and bug fixes. 
 
#setwd("/Users/cmonserr/OneDrive - UPV/Trabajo_2/Asignaturas/Evaluacion de modelos/Practicas/Practica 3/Bike-Sharing-Dataset")
days <- read.csv("day.csv")
hour <- read.csv("hour.csv")

days$dteday <- as_date(days$dteday)
days_since <- select(days, workingday, holiday, temp, hum, windspeed, cnt)
days_since$days_since_2011 <- int_length(interval(ymd("2011-01-01"), days$dteday)) / (3600*24)
days_since$SUMMER <- ifelse(days$season == 3, 1, 0)
days_since$FALL <- ifelse(days$season == 4, 1, 0)
days_since$WINTER <- ifelse(days$season == 1, 1, 0)
days_since$MISTY <- ifelse(days$weathersit == 2, 1, 0)
days_since$RAIN <- ifelse(days$weathersit == 3 | days$weathersit == 4, 1, 0)
days_since$temp <- days_since$temp * 47 - 8
days_since$hum <- days_since$hum * 100
days_since$windspeed <- days_since$windspeed * 67

rf <- rfsrc(cnt~., data=days_since)

results <- select(days_since, days_since_2011, temp, hum, windspeed, cnt)
nr <- nrow(days_since)
for(c in names(results)[1:4])
{
  for(i in 1:nr){
    r <- days_since
    r[[c]] <- days_since[[c]][i]
    sal <- predict(rf, r)$predicted
    results[[c]][i] <- sum(sal) / nr
  }
}

g1 <- ggplot(days_since, aes(x=days_since_2011, y=results$days_since_2011)) + geom_line()+ ylim(c(0,6000)) + geom_rug(alpha=0.1, sides="b") + ylab("Prediction") + xlab("Days since 2011")

g2 <- ggplot(days_since, aes(x=temp, y=results$temp)) + geom_line() + ylim(c(0,6000)) + geom_rug(alpha=0.1, sides="b") + xlab("Temperature")

g3 <- ggplot(days_since, aes(x=hum, y=results$hum)) + geom_line() + ylim(c(0,6000)) + geom_rug(alpha=0.1, sides="b") + xlab("Humidity")

g4 <- ggplot(days_since, aes(x=windspeed, y=results$windspeed)) + geom_line() + ylim(c(0,6000)) + geom_rug(alpha=0.1, sides="b")+ xlab("Wind speed")

subplot(g1, g2, g3, g4, shareX = FALSE, shareY = TRUE, titleX = TRUE)
NA

Interpretación

EXERCISE:

Generate a 2D Partial Dependency Plot with humidity and temperature to predict the number of bikes rented depending of those parameters.

BE CAREFUL: due to the size, extract a set of random samples from the BBDD before generating the the data for the Partial Dependency Plot.

Show the density distribution of both input features with the 2D plot as shown in the class slides.

TIP: Use geom_tile() to generate the 2D plot. Set width and height to avoid holes.

QUESTION:

Interpret the results.


sampled <- sample_n(days_since, 40)
temp <- sampled$temp
hum <- sampled$hum
th <- inner_join(data.frame(temp),data.frame(hum), by=character())
th$p <- 0

for(i in 1:nrow(th)){
  r <- days_since
  r[["temp"]] <- th[["temp"]][i]
  r[["hum"]] <- th[["hum"]][i]
  
  sal <- predict(rf, r)$predicted
  th[["p"]][i] <- sum(sal) / nr
}

ggplot(th, aes(x=temp, y=hum)) + geom_tile(aes(fill=p, width=10, height=15)) + geom_rug(alpha=0.01)

Interpretacion

Podemos apreciar una cierta independencia entre el efecto de la temperatura y el de la humedad, ya que, como se ha podido observar en el PDP en 1 dimensión de ambas variables, mientras la humedad se mantenga por debajo del 50% el número de bicicletas vendidas será máximo, y a partir de ese umbral irá disminuyendo el número de ventas según aumente la humedad. Por otro lado, en el caso de la temperatura, partiendo desde la temperatura mínima, según aumenta la temperatura aumenta el número de ventas, hasta llegar a un máximo sobre los 16 grados, donde el número de bicicletas vendidas se mantiene constante hasta llegar a los 26 grados. A partir de ahí vuelve a disminuir el número de bicicletas vendidas según aumenta la temperatura.

En el PDP en 2D podemos apreciar estos mismos fenómenos de manera idéntica. Vemos que el número máximo de ventas se alcanzará cuando la temperatura sea agradable (en torno a 20 grados) y la humedad sea menor que el 50%; mientras que este número alcanzará su mínimo cuando la temperatura sea extremadamente baja y la humedad muy alta.

Asumimos que los efectos de ambas variables son independientes porque estos mismos efectos se pueden apreciar por separado, sin hacer uso del PDP en 2 dimensiones, y por tanto no es de interés estudiar su interacción.

También hay que tener en consideración que no se tienen muchas observaciones reales de todos los valores que se han probado, con lo cual el modelo no se ha podido entrenar en esos escenarios y las explicaciones pueden no ser fiables. Es el caso de la humedad cuando toma valores por debajo del 37% o por encima del 92%; o de la temperatura cuando está por debajo de 0.5 o por encima de 29 grados. Además, cabe mencionar que no se tienen datos tampoco de cuántas observaciones del conjunto se tienen para cada posible caso de interacción de ambas variables.

EXERCISE:

Apply the previous concepts to predict the price of a house from the database kc_house_data.csv. In this case, use again a random forest approximation for the prediction based on the features bedrooms, bathrooms, sqft_living, sqft_lot, floors and yr_built. Use the partial dependence plot to visualize the relationships the model learned.

BE CAREFUL: due to the size, extract a set of random samples from the BBDD before generating the data for the Partial Dependency Plot.

QUESTION:

Analyse the influence of bedrooms, bathrooms, sqft_living and floors on the predicted price.


d <- read.csv("kc_house_data.csv")

set.seed(50)

sampled <- sample_n(d, 1000)

sampled <- select(sampled, bedrooms, bathrooms, sqft_living, sqft_lot, floors, yr_built, price)

rf <- rfsrc(price~., data=sampled)

results <- select(sampled, bedrooms, bathrooms, sqft_living, floors, price)
nr <- nrow(sampled)
for(c in names(results)[1:4])
{
  for(i in 1:nr){
    r <- sampled
    r[[c]] <- sampled[[c]][i]
    sal <- predict(rf, r)$predicted
    results[[c]][i] <- sum(sal) / nr
  }
}

g1 <- ggplot(sampled, aes(x=bedrooms, y=results$bedrooms)) + geom_line() + geom_rug(alpha=0.1, sides="b") + ylab("Prediction") + xlab("Bedrooms")

g2 <- ggplot(sampled, aes(x=bathrooms, y=results$bathrooms)) + geom_line() + geom_rug(alpha=0.1, sides="b") + xlab("Bathrooms")

g3 <- ggplot(sampled, aes(x=sqft_living, y=results$sqft_living)) + geom_line() + geom_rug(alpha=0.1, sides="b") + xlab("Sqft Living")

g4 <- ggplot(sampled, aes(x=floors, y=results$floors)) + geom_line() + geom_rug(alpha=0.1, sides="b")+ xlab("Floors")

subplot(g1, g2, g3, g4, titleX = TRUE, shareX = FALSE)

Interpretacion

LS0tDQp0aXRsZTogIlhBSSAzOiBNb2RlbC1BZ25vc3RpYyBtZXRob2RzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KKipBdXRob3JzKioNCg0KLSBGcmFuY2VzY28gU2dyZWxsaQ0KLSDDgWx2YXJvIE3DrW5ndWV6IEJlbGzDs24NCg0KIyMgRVhFUkNJU0U6DQoNCkFwcGx5IFBEUCB0byB0aGUgcmVncmVzc2lvbiBleGFtcGxlIG9mIHByZWRpY3RpbmcgYmlrZSByZW50YWxzLiBGaXQgYSByYW5kb20gZm9yZXN0IGFwcHJveGltYXRpb24gZm9yIHRoZSBwcmVkaWN0aW9uIG9mIGJpa2UgcmVudGFscyAoKipjbnQqKikuIFVzZSB0aGUgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3QgdG8gdmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXBzIHRoZSBtb2RlbCBsZWFybmVkLiBVc2UgdGhlIHNsaWRlcyBzaG93biBpbiBjbGFzcyBhcyBtb2RlbC4gIA0KDQojIyBRVUVTVElPTjoNCg0KQW5hbHlzZSB0aGUgaW5mbHVlbmNlIG9mICoqZGF5cyBzaW5jZSAyMDExLCB0ZW1wZXJhdHVyZSwgaHVtaWRpdHkqKiBhbmQgKip3aW5kIHNwZWVkKiogb24gdGhlIHByZWRpY3RlZCBiaWtlIGNvdW50cy4NCg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkocmFuZG9tRm9yZXN0U1JDKQ0KDQojc2V0d2QoIi9Vc2Vycy9jbW9uc2Vyci9PbmVEcml2ZSAtIFVQVi9UcmFiYWpvXzIvQXNpZ25hdHVyYXMvRXZhbHVhY2lvbiBkZSBtb2RlbG9zL1ByYWN0aWNhcy9QcmFjdGljYSAzL0Jpa2UtU2hhcmluZy1EYXRhc2V0IikNCmRheXMgPC0gcmVhZC5jc3YoImRheS5jc3YiKQ0KaG91ciA8LSByZWFkLmNzdigiaG91ci5jc3YiKQ0KDQpkYXlzJGR0ZWRheSA8LSBhc19kYXRlKGRheXMkZHRlZGF5KQ0KZGF5c19zaW5jZSA8LSBzZWxlY3QoZGF5cywgd29ya2luZ2RheSwgaG9saWRheSwgdGVtcCwgaHVtLCB3aW5kc3BlZWQsIGNudCkNCmRheXNfc2luY2UkZGF5c19zaW5jZV8yMDExIDwtIGludF9sZW5ndGgoaW50ZXJ2YWwoeW1kKCIyMDExLTAxLTAxIiksIGRheXMkZHRlZGF5KSkgLyAoMzYwMCoyNCkNCmRheXNfc2luY2UkU1VNTUVSIDwtIGlmZWxzZShkYXlzJHNlYXNvbiA9PSAzLCAxLCAwKQ0KZGF5c19zaW5jZSRGQUxMIDwtIGlmZWxzZShkYXlzJHNlYXNvbiA9PSA0LCAxLCAwKQ0KZGF5c19zaW5jZSRXSU5URVIgPC0gaWZlbHNlKGRheXMkc2Vhc29uID09IDEsIDEsIDApDQpkYXlzX3NpbmNlJE1JU1RZIDwtIGlmZWxzZShkYXlzJHdlYXRoZXJzaXQgPT0gMiwgMSwgMCkNCmRheXNfc2luY2UkUkFJTiA8LSBpZmVsc2UoZGF5cyR3ZWF0aGVyc2l0ID09IDMgfCBkYXlzJHdlYXRoZXJzaXQgPT0gNCwgMSwgMCkNCmRheXNfc2luY2UkdGVtcCA8LSBkYXlzX3NpbmNlJHRlbXAgKiA0NyAtIDgNCmRheXNfc2luY2UkaHVtIDwtIGRheXNfc2luY2UkaHVtICogMTAwDQpkYXlzX3NpbmNlJHdpbmRzcGVlZCA8LSBkYXlzX3NpbmNlJHdpbmRzcGVlZCAqIDY3DQoNCnJmIDwtIHJmc3JjKGNudH4uLCBkYXRhPWRheXNfc2luY2UpDQoNCnJlc3VsdHMgPC0gc2VsZWN0KGRheXNfc2luY2UsIGRheXNfc2luY2VfMjAxMSwgdGVtcCwgaHVtLCB3aW5kc3BlZWQsIGNudCkNCm5yIDwtIG5yb3coZGF5c19zaW5jZSkNCmZvcihjIGluIG5hbWVzKHJlc3VsdHMpWzE6NF0pDQp7DQogIGZvcihpIGluIDE6bnIpew0KICAgIHIgPC0gZGF5c19zaW5jZQ0KICAgIHJbW2NdXSA8LSBkYXlzX3NpbmNlW1tjXV1baV0NCiAgICBzYWwgPC0gcHJlZGljdChyZiwgcikkcHJlZGljdGVkDQogICAgcmVzdWx0c1tbY11dW2ldIDwtIHN1bShzYWwpIC8gbnINCiAgfQ0KfQ0KDQpnMSA8LSBnZ3Bsb3QoZGF5c19zaW5jZSwgYWVzKHg9ZGF5c19zaW5jZV8yMDExLCB5PXJlc3VsdHMkZGF5c19zaW5jZV8yMDExKSkgKyBnZW9tX2xpbmUoKSsgeWxpbShjKDAsNjAwMCkpICsgZ2VvbV9ydWcoYWxwaGE9MC4xLCBzaWRlcz0iYiIpICsgeWxhYigiUHJlZGljdGlvbiIpICsgeGxhYigiRGF5cyBzaW5jZSAyMDExIikNCg0KZzIgPC0gZ2dwbG90KGRheXNfc2luY2UsIGFlcyh4PXRlbXAsIHk9cmVzdWx0cyR0ZW1wKSkgKyBnZW9tX2xpbmUoKSArIHlsaW0oYygwLDYwMDApKSArIGdlb21fcnVnKGFscGhhPTAuMSwgc2lkZXM9ImIiKSArIHhsYWIoIlRlbXBlcmF0dXJlIikNCg0KZzMgPC0gZ2dwbG90KGRheXNfc2luY2UsIGFlcyh4PWh1bSwgeT1yZXN1bHRzJGh1bSkpICsgZ2VvbV9saW5lKCkgKyB5bGltKGMoMCw2MDAwKSkgKyBnZW9tX3J1ZyhhbHBoYT0wLjEsIHNpZGVzPSJiIikgKyB4bGFiKCJIdW1pZGl0eSIpDQoNCmc0IDwtIGdncGxvdChkYXlzX3NpbmNlLCBhZXMoeD13aW5kc3BlZWQsIHk9cmVzdWx0cyR3aW5kc3BlZWQpKSArIGdlb21fbGluZSgpICsgeWxpbShjKDAsNjAwMCkpICsgZ2VvbV9ydWcoYWxwaGE9MC4xLCBzaWRlcz0iYiIpKyB4bGFiKCJXaW5kIHNwZWVkIikNCg0Kc3VicGxvdChnMSwgZzIsIGczLCBnNCwgc2hhcmVYID0gRkFMU0UsIHNoYXJlWSA9IFRSVUUsIHRpdGxlWCA9IFRSVUUpDQoNCmBgYA0KDQoqKkludGVycHJldGFjacOzbioqDQoNCi0gYERheXMgc2luY2UgMjAxMWA6IEEgbWVkaWRhIHF1ZSBwYXNhbiBtw6FzIGTDrWFzIGRlc2RlIDIwMTEsIGVuIGdlbmVyYWwsIHNlIHRpZW5kZW4gYSB2ZW5kZXIgY2FkYSB2ZXogbcOhcyBiaWNpY2xldGFzLiBObyBvYnN0YW50ZSwgaGF5IHF1ZSBtZW5jaW9uYXIgZG9zIGFzcGVjdG9zIGltcG9ydGFudGVzLiBFbCBwcmltZXJvIGVzIHF1ZSBlbnRyZSBsb3MgMTMwIHkgbG9zIDM1MCBkw61hcywgbGEgZGlmZXJlbmNpYSBkZSBsYSBpbXBvcnRhbmNpYSBlcyBtw61uaW1hIGVuIGVzdGEgdmFyaWFibGUgKHNpZW1wcmUgZW4gdG9ybm8gYSAzNzAwLTM4MDAgYmljaWNsZXRhcyBtw6FzKS4gRWwgc2VndW5kbyBlcyBxdWUgbGEgdGVuZGVuY2lhIGVzIHNpZW1wcmUgY3JlY2llbnRlIGEgZXhjZXBjacOzbiBkZWwgdHJhbW8gZmluYWwsIHB1ZXMgYSBwYXJ0aXIgZGUgbG9zIDY2MiBkw61hcyBzZSBwYXNhIGRlIHZlbmRlciA1ODE0IGJpY2ljbGV0YXMgbcOhcyBhIHZlbmRlciA0Nzk5LCBlcyBkZWNpciBxdWUgbGEgaW5mbHVlbmNpYSBkZWwgcGFzbyBkZWwgdGllbXBvIGRlamEgZGUgc2VyIHNpZW1wcmUgY3JlY2llbnRlLg0KRXN0YXMgZXhwbGljYWNpb25lcyBzb24gZmlhYmxlcywgcHVlcyBzZSB0aWVuZW4gb2JzZXJ2YWNpb25lcyBwYXJhIHRvZG9zIGxvcyB2YWxvcmVzIGRlIGVzdGEgdmFyaWFibGUuDQoNCi0gYFRlbXBlcmF0dXJlYDogTG8gbcOhcyBpbXBvcnRhbnRlIGVuIGVzdGEgdmFyaWFibGUgZXMgcXVlLCBlbXBlemFuZG8gcG9yIGVsIHZhbG9yIG3DoXMgYmFqbyAoNSBncmFkb3MgYmFqbyAwKSwgc2Vnw7puIGxhIHRlbXBlcmF0dXJhIGF1bWVudGEsIHNlIHZlbmRlbiBtw6FzIGJpY2ljbGV0YXMgKHBhc2FuZG8gZGUgdmVuZGVyIDMwOTQgYmljaWNsZXRhcyBjb24gLTUgZ3JhZG9zIGhhc3RhIDUxMjYgYmljaWNsZXRhcyBjb24gMTYgZ3JhZG9zKS4gTm8gb2JzdGFudGUsIGVzdGEgdGVuZGVuY2lhIGNyZWNpZW50ZSBzZSBmcmVuYSBlbiBsb3MgMTYgZ3JhZG9zLCBwYXJhIG1hbnRlbmVyc2UgZXN0YWJsZSBoYXN0YSBsb3MgMjYgZ3JhZG9zIGFwcm94aW1hZGFtZW50ZS4gQSBwYXJ0aXIgZGUgZXNhIHRlbXBlcmF0dXJhLCBwb3IgY2FkYSBncmFkbyBxdWUgYXVtZW50YSBsYSB0ZW1wZXJhdHVyYSBzZSB2ZW5kZXLDoW4gbWVub3MgYmljaWNsZXRhcy4NCkVuIG90cmFzIHBhbGFicmFzLCBzZSB2ZW5kZXLDoW4gbcOhcyBiaWNpY2xldGFzIGN1YW5kbyBsYSB0ZW1wZXJhdHVyYSBzZWEgYWdyYWRhYmxlICgxNi0yNiBncmFkb3MpLiBDdWFuZG8gaGFjZSBmcsOtbyBvIGNhbG9yLCBzZSB2ZW5kZW4gY2FkYSB2ZXogbWVub3MgYmljaWNsZXRhcy4NCkZpbmFsbWVudGUsIGhheSBxdWUgdGVuZXIgY3VpZGFkbyBjb24gbGFzIGV4cGxpY2FjaW9uZXMgY3VhbmRvIGxhcyB0ZW1wZXJhdHVyYXMgc29uIG1lbm9yZXMgcXVlIDAuNSBncmFkb3MgbyBtYXlvcmVzIHF1ZSAyOSwgcHVlcyBzZSB0aWVuZW4gbWVub3Mgb2JzZXJ2YWNpb25lcyBjdWFuZG8gbGEgdGVtcGVyYXR1cmEgdG9tYSBlc29zIHZhbG9yZXMuDQoNCi0gYEh1bWlkaXR5YDogTWllbnRyYXMgbGEgaHVtZWRhZCBlcyBtZW5vciBxdWUgNTAgZWwgbsO6bWVybyBkZSBiaWNpY2xldGFzIHZlbmRpZGFzIHNlIG1hbnRlbmRyw6Egc2llbXByZSBjb25zdGFudGUgZW4gNDcwMCBiaWNpY2xldGFzIGFwcm94aW1hZGFtZW50ZS4gVW5hIHZleiBzdXBlcmFkbyBlc2UgdW1icmFsIGRlbCA1MCUgZGUgaHVtZWRhZCwgc2Vnw7puIGF1bWVudGEgbGEgaHVtZWRhZCwgc2UgdmVuZGVuIGNhZGEgdmV6IG1lbm9zIGJpY2ljbGV0YXMsIGhhc3RhIGxsZWdhciBhIGxhcyAzNDkwIGJpY2ljbGV0YXMgdmVuZGlkYXMgY3VhbmRvIGxhIGh1bWVkYWQgZXMgZGVsIDk3JS4NCk5vIG9ic3RhbnRlLCBlc3RhcyBleHBsaWNhY2lvbmVzIGRlYmVuIHNlciByZXZpc2FkYXMgY3VhbmRvIGxhIGh1bWVkYWQgc2UgZW5jdWVudHJhIHBvciBkZWJham8gZGVsIDM3JSBvIHBvciBlbmNpbWEgZGVsIDkyJSwgcHVlcyBlbiBlc29zIGNhc29zIG5vIHNlIHRpZW5lbiBtdWNoYXMgb2JzZXJ2YWNpb25lcywgcG9yIGxvIHF1ZSBsYXMgZXhwbGljYWNpb25lcyBlbiBlc29zIGNhc29zIG5vIHNvbiBkZWwgdG9kbyBmaWFibGVzLg0KDQotIGBXaW5kIHNwZWVkYDogRWwgbW9kZWxvIHByZWRpY2UgcXVlIGxhIHRlbmRlbmNpYSBkZSBlc3RhIHZhcmlhYmxlIGVzIGNsYXJhbWVudGUgZGVjcmVjaWVudGUuIFNlZ8O6biBhdW1lbnRhIGVsIHZpZW50byBjYWRhIHZleiBzZSB2ZW5kZW4gbWVub3MgYmljaWNsZXRhcywgcGFzYW5kbyBkZSA0NjQwIGN1YW5kbyBlbCB2aWVudG8gdG9tYSB1biB2YWxvciBkZSAxLjUsIGEgMzk5MiBjdWFuZG8gZWwgdmllbnRvIHRvbWEgdW4gdmFsb3IgZGUgMjQuIFBhcmEgdmFsb3JlcyBtYXlvcmVzIHF1ZSAyNCwgZWwgbsO6bWVybyBkZSBiaWNpY2xldGFzIHZlbmRpZGFzIHNlIG1hbnRlbmRyw6EgZXN0YWJsZSBlbiBlc2UgdmFsb3IuDQpObyBvYnN0YW50ZSwgcGFyYSB2YWxvcmVzIG1heW9yZXMgcXVlIDI0LCBoYXkgcXVlIHJlY29uc2lkZXJhciBsYSBmaWFiaWxpZGFkIGRlIGVzdGFzIGV4cGxpY2FjaW9uZXMsIHB1ZXMgc2UgdGllbmVuIG11eSBwb2NhcyAobyBuaW5ndW5hKSBvYnNlcnZhY2lvbmVzIHBhcmEgZXNvcyB2YWxvcmVzIGVuIGVzdGEgdmFyaWFibGUuDQoNCiMjIEVYRVJDSVNFOg0KDQpHZW5lcmF0ZSBhIDJEIFBhcnRpYWwgRGVwZW5kZW5jeSBQbG90IHdpdGggaHVtaWRpdHkgYW5kIHRlbXBlcmF0dXJlIHRvIHByZWRpY3QgdGhlIG51bWJlciBvZiBiaWtlcyByZW50ZWQgZGVwZW5kaW5nIG9mIHRob3NlIHBhcmFtZXRlcnMuDQoNCkJFIENBUkVGVUw6IGR1ZSB0byB0aGUgc2l6ZSwgZXh0cmFjdCBhIHNldCBvZiByYW5kb20gc2FtcGxlcyBmcm9tIHRoZSBCQkREIGJlZm9yZSBnZW5lcmF0aW5nIHRoZSB0aGUgZGF0YSBmb3IgdGhlIFBhcnRpYWwgRGVwZW5kZW5jeSBQbG90LiANCg0KU2hvdyB0aGUgZGVuc2l0eSBkaXN0cmlidXRpb24gb2YgYm90aCBpbnB1dCBmZWF0dXJlcyB3aXRoIHRoZSAyRCBwbG90IGFzIHNob3duIGluIHRoZSBjbGFzcyBzbGlkZXMuIA0KDQpUSVA6IFVzZSBnZW9tX3RpbGUoKSB0byBnZW5lcmF0ZSB0aGUgMkQgcGxvdC4gU2V0IHdpZHRoIGFuZCBoZWlnaHQgdG8gYXZvaWQgaG9sZXMuIA0KDQojIyBRVUVTVElPTjoNCg0KSW50ZXJwcmV0IHRoZSByZXN1bHRzLg0KDQoNCmBgYHtyfQ0KDQpzYW1wbGVkIDwtIHNhbXBsZV9uKGRheXNfc2luY2UsIDQwKQ0KdGVtcCA8LSBzYW1wbGVkJHRlbXANCmh1bSA8LSBzYW1wbGVkJGh1bQ0KdGggPC0gaW5uZXJfam9pbihkYXRhLmZyYW1lKHRlbXApLGRhdGEuZnJhbWUoaHVtKSwgYnk9Y2hhcmFjdGVyKCkpDQp0aCRwIDwtIDANCg0KZm9yKGkgaW4gMTpucm93KHRoKSl7DQogIHIgPC0gZGF5c19zaW5jZQ0KICByW1sidGVtcCJdXSA8LSB0aFtbInRlbXAiXV1baV0NCiAgcltbImh1bSJdXSA8LSB0aFtbImh1bSJdXVtpXQ0KICANCiAgc2FsIDwtIHByZWRpY3QocmYsIHIpJHByZWRpY3RlZA0KICB0aFtbInAiXV1baV0gPC0gc3VtKHNhbCkgLyBucg0KfQ0KDQpnZ3Bsb3QodGgsIGFlcyh4PXRlbXAsIHk9aHVtKSkgKyBnZW9tX3RpbGUoYWVzKGZpbGw9cCwgd2lkdGg9MTAsIGhlaWdodD0xNSkpICsgZ2VvbV9ydWcoYWxwaGE9MC4wMSkNCmBgYA0KDQoqKkludGVycHJldGFjaW9uKioNCg0KUG9kZW1vcyBhcHJlY2lhciB1bmEgY2llcnRhIGluZGVwZW5kZW5jaWEgZW50cmUgZWwgZWZlY3RvIGRlIGxhIHRlbXBlcmF0dXJhIHkgZWwgZGUgbGEgaHVtZWRhZCwgeWEgcXVlLCBjb21vIHNlIGhhIHBvZGlkbyBvYnNlcnZhciBlbiBlbCBQRFAgZW4gMSBkaW1lbnNpw7NuIGRlIGFtYmFzIHZhcmlhYmxlcywgbWllbnRyYXMgbGEgaHVtZWRhZCBzZSBtYW50ZW5nYSBwb3IgZGViYWpvIGRlbCA1MCUgZWwgbsO6bWVybyBkZSBiaWNpY2xldGFzIHZlbmRpZGFzIHNlcsOhIG3DoXhpbW8sIHkgYSBwYXJ0aXIgZGUgZXNlIHVtYnJhbCBpcsOhIGRpc21pbnV5ZW5kbyBlbCBuw7ptZXJvIGRlIHZlbnRhcyBzZWfDum4gYXVtZW50ZSBsYSBodW1lZGFkLiBQb3Igb3RybyBsYWRvLCBlbiBlbCBjYXNvIGRlIGxhIHRlbXBlcmF0dXJhLCBwYXJ0aWVuZG8gZGVzZGUgbGEgdGVtcGVyYXR1cmEgbcOtbmltYSwgc2Vnw7puIGF1bWVudGEgbGEgdGVtcGVyYXR1cmEgYXVtZW50YSBlbCBuw7ptZXJvIGRlIHZlbnRhcywgaGFzdGEgbGxlZ2FyIGEgdW4gbcOheGltbyBzb2JyZSBsb3MgMTYgZ3JhZG9zLCBkb25kZSBlbCBuw7ptZXJvIGRlIGJpY2ljbGV0YXMgdmVuZGlkYXMgc2UgbWFudGllbmUgY29uc3RhbnRlIGhhc3RhIGxsZWdhciBhIGxvcyAyNiBncmFkb3MuIEEgcGFydGlyIGRlIGFow60gdnVlbHZlIGEgZGlzbWludWlyIGVsIG7Dum1lcm8gZGUgYmljaWNsZXRhcyB2ZW5kaWRhcyBzZWfDum4gYXVtZW50YSBsYSB0ZW1wZXJhdHVyYS4NCg0KRW4gZWwgUERQIGVuIDJEIHBvZGVtb3MgYXByZWNpYXIgZXN0b3MgbWlzbW9zIGZlbsOzbWVub3MgZGUgbWFuZXJhIGlkw6ludGljYS4gVmVtb3MgcXVlIGVsIG7Dum1lcm8gbcOheGltbyBkZSB2ZW50YXMgc2UgYWxjYW56YXLDoSBjdWFuZG8gbGEgdGVtcGVyYXR1cmEgc2VhIGFncmFkYWJsZSAoZW4gdG9ybm8gYSAyMCBncmFkb3MpIHkgbGEgaHVtZWRhZCBzZWEgbWVub3IgcXVlIGVsIDUwJTsgbWllbnRyYXMgcXVlIGVzdGUgbsO6bWVybyBhbGNhbnphcsOhIHN1IG3DrW5pbW8gY3VhbmRvIGxhIHRlbXBlcmF0dXJhIHNlYSBleHRyZW1hZGFtZW50ZSBiYWphIHkgbGEgaHVtZWRhZCBtdXkgYWx0YS4NCg0KQXN1bWltb3MgcXVlIGxvcyBlZmVjdG9zIGRlIGFtYmFzIHZhcmlhYmxlcyBzb24gaW5kZXBlbmRpZW50ZXMgcG9ycXVlIGVzdG9zIG1pc21vcyBlZmVjdG9zIHNlIHB1ZWRlbiBhcHJlY2lhciBwb3Igc2VwYXJhZG8sIHNpbiBoYWNlciB1c28gZGVsIFBEUCBlbiAyIGRpbWVuc2lvbmVzLCB5IHBvciB0YW50byBubyBlcyBkZSBpbnRlcsOpcyBlc3R1ZGlhciBzdSBpbnRlcmFjY2nDs24uDQoNClRhbWJpw6luIGhheSBxdWUgdGVuZXIgZW4gY29uc2lkZXJhY2nDs24gcXVlIG5vIHNlIHRpZW5lbiBtdWNoYXMgb2JzZXJ2YWNpb25lcyByZWFsZXMgZGUgdG9kb3MgbG9zIHZhbG9yZXMgcXVlIHNlIGhhbiBwcm9iYWRvLCBjb24gbG8gY3VhbCBlbCBtb2RlbG8gbm8gc2UgaGEgcG9kaWRvIGVudHJlbmFyIGVuIGVzb3MgZXNjZW5hcmlvcyB5IGxhcyBleHBsaWNhY2lvbmVzIHB1ZWRlbiBubyBzZXIgZmlhYmxlcy4gRXMgZWwgY2FzbyBkZSBsYSBodW1lZGFkIGN1YW5kbyB0b21hIHZhbG9yZXMgcG9yIGRlYmFqbyBkZWwgMzclIG8gcG9yIGVuY2ltYSBkZWwgOTIlOyBvIGRlIGxhIHRlbXBlcmF0dXJhIGN1YW5kbyBlc3TDoSBwb3IgZGViYWpvIGRlIDAuNSBvIHBvciBlbmNpbWEgZGUgMjkgZ3JhZG9zLg0KQWRlbcOhcywgY2FiZSBtZW5jaW9uYXIgcXVlIG5vIHNlIHRpZW5lbiBkYXRvcyB0YW1wb2NvIGRlIGN1w6FudGFzIG9ic2VydmFjaW9uZXMgZGVsIGNvbmp1bnRvIHNlIHRpZW5lbiBwYXJhIGNhZGEgcG9zaWJsZSBjYXNvIGRlIGludGVyYWNjacOzbiBkZSBhbWJhcyB2YXJpYWJsZXMuDQoNCiMjIEVYRVJDSVNFOg0KDQpBcHBseSB0aGUgcHJldmlvdXMgY29uY2VwdHMgdG8gcHJlZGljdCB0aGUgKipwcmljZSoqIG9mIGEgaG91c2UgZnJvbSB0aGUgZGF0YWJhc2UgKiprY19ob3VzZV9kYXRhLmNzdioqLiBJbiB0aGlzIGNhc2UsIHVzZSBhZ2FpbiBhIHJhbmRvbSBmb3Jlc3QgYXBwcm94aW1hdGlvbiBmb3IgdGhlIHByZWRpY3Rpb24gYmFzZWQgb24gdGhlIGZlYXR1cmVzICoqYmVkcm9vbXMqKiwgKipiYXRocm9vbXMqKiwgKipzcWZ0X2xpdmluZyoqLCAqKnNxZnRfbG90KiosICoqZmxvb3JzKiogYW5kICoqeXJfYnVpbHQqKi4gDQpVc2UgdGhlIHBhcnRpYWwgZGVwZW5kZW5jZSBwbG90IHRvIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwcyB0aGUgbW9kZWwgbGVhcm5lZC4NCg0KQkUgQ0FSRUZVTDogZHVlIHRvIHRoZSBzaXplLCBleHRyYWN0IGEgc2V0IG9mIHJhbmRvbSBzYW1wbGVzIGZyb20gdGhlIEJCREQgYmVmb3JlIGdlbmVyYXRpbmcgdGhlIGRhdGEgZm9yIHRoZSBQYXJ0aWFsIERlcGVuZGVuY3kgUGxvdC4gDQoNCiMjIFFVRVNUSU9OOg0KDQpBbmFseXNlIHRoZSBpbmZsdWVuY2Ugb2YgKipiZWRyb29tcywgYmF0aHJvb21zLCBzcWZ0X2xpdmluZyoqIGFuZCAqKmZsb29ycyoqIG9uIHRoZSBwcmVkaWN0ZWQgcHJpY2UuDQoNCg0KYGBge3J9DQoNCmQgPC0gcmVhZC5jc3YoImtjX2hvdXNlX2RhdGEuY3N2IikNCg0Kc2V0LnNlZWQoNTApDQoNCnNhbXBsZWQgPC0gc2FtcGxlX24oZCwgMTAwMCkNCg0Kc2FtcGxlZCA8LSBzZWxlY3Qoc2FtcGxlZCwgYmVkcm9vbXMsIGJhdGhyb29tcywgc3FmdF9saXZpbmcsIHNxZnRfbG90LCBmbG9vcnMsIHlyX2J1aWx0LCBwcmljZSkNCg0KcmYgPC0gcmZzcmMocHJpY2V+LiwgZGF0YT1zYW1wbGVkKQ0KDQpyZXN1bHRzIDwtIHNlbGVjdChzYW1wbGVkLCBiZWRyb29tcywgYmF0aHJvb21zLCBzcWZ0X2xpdmluZywgZmxvb3JzLCBwcmljZSkNCm5yIDwtIG5yb3coc2FtcGxlZCkNCmZvcihjIGluIG5hbWVzKHJlc3VsdHMpWzE6NF0pDQp7DQogIGZvcihpIGluIDE6bnIpew0KICAgIHIgPC0gc2FtcGxlZA0KICAgIHJbW2NdXSA8LSBzYW1wbGVkW1tjXV1baV0NCiAgICBzYWwgPC0gcHJlZGljdChyZiwgcikkcHJlZGljdGVkDQogICAgcmVzdWx0c1tbY11dW2ldIDwtIHN1bShzYWwpIC8gbnINCiAgfQ0KfQ0KDQpnMSA8LSBnZ3Bsb3Qoc2FtcGxlZCwgYWVzKHg9YmVkcm9vbXMsIHk9cmVzdWx0cyRiZWRyb29tcykpICsgZ2VvbV9saW5lKCkgKyBnZW9tX3J1ZyhhbHBoYT0wLjEsIHNpZGVzPSJiIikgKyB5bGFiKCJQcmVkaWN0aW9uIikgKyB4bGFiKCJCZWRyb29tcyIpDQoNCmcyIDwtIGdncGxvdChzYW1wbGVkLCBhZXMoeD1iYXRocm9vbXMsIHk9cmVzdWx0cyRiYXRocm9vbXMpKSArIGdlb21fbGluZSgpICsgZ2VvbV9ydWcoYWxwaGE9MC4xLCBzaWRlcz0iYiIpICsgeGxhYigiQmF0aHJvb21zIikNCg0KZzMgPC0gZ2dwbG90KHNhbXBsZWQsIGFlcyh4PXNxZnRfbGl2aW5nLCB5PXJlc3VsdHMkc3FmdF9saXZpbmcpKSArIGdlb21fbGluZSgpICsgZ2VvbV9ydWcoYWxwaGE9MC4xLCBzaWRlcz0iYiIpICsgeGxhYigiU3FmdCBMaXZpbmciKQ0KDQpnNCA8LSBnZ3Bsb3Qoc2FtcGxlZCwgYWVzKHg9Zmxvb3JzLCB5PXJlc3VsdHMkZmxvb3JzKSkgKyBnZW9tX2xpbmUoKSArIGdlb21fcnVnKGFscGhhPTAuMSwgc2lkZXM9ImIiKSsgeGxhYigiRmxvb3JzIikNCg0Kc3VicGxvdChnMSwgZzIsIGczLCBnNCwgdGl0bGVYID0gVFJVRSwgc2hhcmVYID0gRkFMU0UpDQpgYGANCg0KKipJbnRlcnByZXRhY2lvbioqDQoNCi0gYGJlZHJvb21zYDogRXN0YSB2YXJpYWJsZSBzZSBjb21wb3J0YSBkZSB1bmEgbWFuZXJhIGN1cmlvc2EsIHB1ZXMsIHBhcnRpZW5kbyBkZSAwIGhhYml0YWNpb25lcywgYSBtw6FzIGhhYml0YWNpb25lcywgbcOhcyBiYXJhdGEgc2Vyw6EgbGEgdml2aWVuZGEsIGhhc3RhIGxsZWdhciBhIDQgaGFiaXRhY2lvbmVzLiBBIHBhcnRpciBkZSBlc2UgcHVudG8gc2UgaW52aWVydGUgbGEgdGVuZGVuY2lhLCB5IGEgbWF5b3IgbsO6bWVybyBkZSBoYWJpdGFjaW9uZXMsIG1heW9yIHByZWNpby4gTm8gb2JzdGFudGUsIG5vIHNlIHB1ZWRlIGFmaXJtYXIgY29uIGZpcm1lemEgZXN0bywgeWEgcXVlIHNlIHRpZW5lbiBwb2NhcyBvYnNlcnZhY2lvbmVzIHBhcmEgbGEgdmFyaWFibGUgY3VhbmRvIHNlIHRpZW5lbiAwIG8gbcOhcyBkZSA2IGhhYml0YWNpb25lcy4NCg0KLSBgYmF0aHJvb21zYDogU2UgcHVlZGUgYXByZWNpYXIgY2xhcmFtZW50ZSBjb21vIGEgbWF5b3IgbsO6bWVybyBkZSBjdWFydG9zIGRlIGJhw7FvcywgbWF5b3Igc2Vyw6EgZWwgcHJlY2lvIGRlIGxhIHZpdmllbmRhIGVuIGN1ZXN0acOzbi4gTm8gb2JzdGFudGUsIGVzdGEgYWZpcm1hY2nDs24gbm8gZXMgZGVsIHRvZG8gZmlhYmxlIHBhcmEgMCBvIG3DoXMgZGUgNCBiYcOxb3MsIHB1ZXMgYXBlbmFzIHNlIHRpZW5lbiBtdWVzdHJhcyBkZSBlbnRyZW5hbWllbnRvIGNvbiBlc3RvcyB2YWxvcmVzLg0KDQotIGBzcWZ0X2xpdmluZ2A6IFBvZGVtb3MgYXByZWNpYXIgY29tbyBjbGFyYW1lbnRlLCBjdcOhbnRvIG1heW9yIHNlYSBsYSBzdXBlcmZpY2llIGRlbCBzYWzDs24sIG3DoXMgY2FyYSBzZXLDoSBsYSB2aXZpZW5kYSBlbiBjdWVzdGnDs24uIE5vIG9ic3RhbnRlLCBlc3RhIGFmaXJtYWNpw7NuIGVzdMOhIGVuIGR1ZGEgcGFyYSB2YWxvcmVzIG1lbm9yZXMgcXVlIDU2MCBwaWVzIGN1YWRyYWRvcyB5IG1heW9yZXMgcXVlIDQ5MDAgcGllcyBjdWFkcmFkb3MuDQoNCi0gYGZsb29yc2A6IEVuIGVzdGEgdmFyaWFibGUgcG9kZW1vcyBhZmlybWFyIHF1ZSBhIG1heW9yIG7Dum1lcm8gZGUgcGlzb3MsIG1heW9yIHByZWNpbyB0ZW5kcsOhIGxhIHZpdmllbmRhLCB5IGVzIHVuYSBleHBsaWNhY2nDs24gZmlhYmxlLCBwdWVzIHNlIHRpZW5lbiBvYnNlcnZhY2lvbmVzIHBhcmEgdG9kb3MgbG9zIHZhbG9yZXMgZW4gZWwgc2V0IGRlIGVudHJlbmFtaWVudG8u